//
// Created by benxb on 2021/10/9.
//

#ifndef BENXDB_DATA_PAGE_H
#define BENXDB_DATA_PAGE_H

#include "storage/page.h"
#include "common/config.h"
#include "type/type.h"
#include "mem/tuple.h"
#include "pair.h"

static constexpr uint64_t DELETE_MASK = (1U << (8 * sizeof(uint32_t) - 1));

namespace benxdb {

/**
 * Data page format:
 *  ---------------------------------------------------------
 *  | HEADER | ... INSERTED TUPLES ... | ... FREE SPACE ... |
 *  ---------------------------------------------------------
 *                                     ^
 *                                     free space pointer
 *
 *  Header format (size in bytes):
 *  ----------------------------------------------------------------------------
 *  | PageId (4)| LSN (4)| PrevPageId (4)| NextPageId (4)| FreeSpacePointer(4) |
 *  ----------------------------------------------------------------------------
 *  ----------------------------------------------------------------
 *  | TupleCount (4) | TupleSize (4) | Tuple_1 Id (4) | Tuple_1 Value | ... |
 *  ----------------------------------------------------------------
 *
 */

class DataPage : public Page {
public:
    /**
   * Initialize the TablePage header.
   * @param page_id the page ID of this table page
   * @param prev_page_id the previous table page ID
   */
    void Init(page_id_t page_id, page_id_t prev_page_id, uint32_t tuple_size);

    /** @return the page ID of this data_ page */
    page_id_t GetDataPageId() { return *reinterpret_cast<page_id_t *>(GetData()); }

    /** @return the page ID of the previous data_ page */
    page_id_t GetPrevPageId() { return *reinterpret_cast<page_id_t *>(GetData() + OFFSET_PREV_PAGE_ID); }

    /** @return the page ID of the next data_ page */
    page_id_t GetNextPageId() { return *reinterpret_cast<page_id_t *>(GetData() + OFFSET_NEXT_PAGE_ID); }

    /** Set the page id of the previous page in the table. */
    void SetPrevPageId(page_id_t prev_page_id) {
        memcpy(GetData() + OFFSET_PREV_PAGE_ID, &prev_page_id, sizeof(page_id_t));
    }

    /** Set the page id of the next page in the table. */
    void SetNextPageId(page_id_t next_page_id) {
        memcpy(GetData() + OFFSET_NEXT_PAGE_ID, &next_page_id, sizeof(page_id_t));
    }

    /**
   * Update a pair in the page.
   * @param offset offset tuple to insert
   * @param tuple tuple to insert
   * @return true if the insert is successful (i.e. there is enough space)
   */
    bool UpdatePair(uint32_t offset, const Pair& pair);

    /**
   * Append a pair into the page end.
   * @param pair tuple to insert
   * @return true if the insert is successful (i.e. there is enough space)
   */
    bool AppendPair(const Pair& pair);

    /** To be called on abort. Rollback a delete, i.e. this reverses a MarkDelete. */
    void Delete(const uint32_t &rid);

    /**
     * Read a tuple from page.
     * @param offset offset of the tuple in this page
     * @param[out] rid rid of the tuple to read
     * @param[out] pair the tuple that was read
     * @return true if the read is successful (i.e. the tuple exists)
     */
    bool GetPair(uint32_t offset, const std::shared_ptr<Pair>& pair);

    /**
     * Read last tuple from page.
     * @param offset offset of the tuple in this block
     * @param[out] pair the tuple that was read
     * @return true if the read is successful (i.e. the tuple exists)
     */
    bool GetLastPair(const std::shared_ptr<Pair>& pair);

private:
    static_assert(sizeof(page_id_t) == 4);

    static constexpr size_t SIZE_DATA_PAGE_HEADER = 24;
    static constexpr size_t OFFSET_PREV_PAGE_ID = 8;
    static constexpr size_t OFFSET_NEXT_PAGE_ID = 12;
    static constexpr size_t OFFSET_PAIR_COUNT = 16;
    static constexpr size_t OFFSET_PAIR_SIZE = 20;

    /** @return pointer to the end of the current free space, see header comment */
    uint32_t* GetFreeSpacePointer() {
        return reinterpret_cast<uint32_t *>(GetData() + SIZE_DATA_PAGE_HEADER + GetPairCount() * GetPairSize());
    }

    /**
   * @note returned tuple count may be an overestimate because some slots may be empty
   * @return at least the number of tuples in this page
   */
    uint32_t GetPairCount() { return *reinterpret_cast<uint32_t *>(GetData() + OFFSET_PAIR_COUNT); }

    /** Set the number of tuples in this page. */
    void SetPairCount(uint32_t pair_count) { memcpy(GetData() + OFFSET_PAIR_COUNT, &pair_count, sizeof(uint32_t)); }

    uint32_t GetFreeSpaceRemaining() {
        return PAGE_SIZE - SIZE_DATA_PAGE_HEADER - GetPairCount() * GetPairSize();
    }

    /** @return tuple size */
    uint32_t GetPairSize() {
        return *reinterpret_cast<uint32_t *>(GetData() + OFFSET_PAIR_SIZE);
    }

    /** Set tuple size at slot slot_num. */
    void SetPairSize(uint32_t size) {
        memcpy(GetData() + OFFSET_PAIR_SIZE, &size, sizeof(uint32_t));
    }
};

}

#endif //BENXDB_DATA_PAGE_H